package com.ideal.manage.esms.service.monitor;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ideal.manage.esms.bean.diagnostic.DiagnoseResultBatch;
import com.ideal.manage.esms.bean.diagnostic.DiagnoseResultInfo;
import com.ideal.manage.esms.bean.diagnostic.QDiagnoseResultInfo;
import com.ideal.manage.esms.bean.esms.EsmsDataBatch;
import com.ideal.manage.esms.bean.esms.QEsmsDataBatch;
import com.ideal.manage.esms.bean.project.*;
import com.ideal.manage.esms.jpa.JpaRepositoryImpl;
import com.ideal.manage.esms.service.ElasticsearchService;
import com.querydsl.core.group.GroupBy;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.index.query.QueryBuilders;
import org.hibernate.SQLQuery;
import org.hibernate.transform.Transformers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.*;

import static com.querydsl.core.group.GroupBy.groupBy;

@Service
public class MonitorPackageService {
    private Logger LOG = LoggerFactory.getLogger(MonitorPackageService.class);

    @Autowired
    private EntityManager entityManager;
    @Autowired
    private TransportClient transportClient;
    @Autowired
    private ElasticsearchService elasticsearchService;

    private JpaRepositoryImpl<ParameterInfo, String> parameterJpaRepository;
    private JpaRepositoryImpl<EsmsDataBatch, Long> esmsDataBatchRepository;
    private JpaRepositoryImpl<DiagnoseResultInfo, String> diagnoseResultInfoRepository;
    private JpaRepositoryImpl<DiagnoseResultBatch, Long> diagnoseResultBatchRepository;
    private JpaRepositoryImpl<EquipmentManage, String> equipmentManageRepository;

    @PostConstruct
    public void init() {
        this.parameterJpaRepository = new JpaRepositoryImpl<>(ParameterInfo.class, entityManager);
        this.esmsDataBatchRepository = new JpaRepositoryImpl<EsmsDataBatch, Long>(EsmsDataBatch.class, entityManager);
        this.diagnoseResultInfoRepository = new JpaRepositoryImpl<DiagnoseResultInfo, String>(DiagnoseResultInfo.class, entityManager);
        this.diagnoseResultBatchRepository = new JpaRepositoryImpl<DiagnoseResultBatch, Long>(DiagnoseResultBatch.class, entityManager);
        this.equipmentManageRepository = new JpaRepositoryImpl<>(EquipmentManage.class, entityManager);
    }

    public List<ParameterInfo> getRealTimeInfo(String equipmentCode) {
        QParameterInfo qParameterInfo = QParameterInfo.parameterInfo;
        List<ParameterInfo> infos = parameterJpaRepository.findAll(qParameterInfo.objName.eq(ParameterInfo.OBJECT_NAME_BATTERY_PACK)
                .and(qParameterInfo.isShow.eq(1))
                .and(qParameterInfo.isValid.eq(1)));

        QEquipmentManage qEquipmentManage = QEquipmentManage.equipmentManage;
        ProjectManage projectManage = equipmentManageRepository.query()
                .select(qEquipmentManage.project)
                .from(qEquipmentManage)
                .where(qEquipmentManage.code.eq(equipmentCode))
                .limit(1)
                .fetchOne();

        QEsmsDataBatch qEsmsDataBatch = QEsmsDataBatch.esmsDataBatch;
        EsmsDataBatch batch = esmsDataBatchRepository.query()
                .select(qEsmsDataBatch)
                .from(qEsmsDataBatch)
                .where(qEsmsDataBatch.type.eq("canbs").and(qEsmsDataBatch.projectIp.eq(projectManage.getIp())))
                .orderBy(qEsmsDataBatch.time.desc())
                .limit(1)
                .fetchOne();
        if (batch == null) {
            return infos;
        }
        Map<String, ParameterInfo> infoMap = new HashMap<>();
        for (ParameterInfo info : infos) {
            infoMap.put(info.getName(), info);
        }
        //总电压 SOH  电流
        //最低单体电压 最低单体电压序号  最高单体电压 最高单体电压序号 电压极差
        //最高温度 最高温度序号 最低温度 最低温度序号 温度极差
        //平均温度
        JSONObject r = elasticsearchService.searchByMatchFunctionNews(QueryBuilders
                        .boolQuery()
                        .filter(QueryBuilders.termQuery("macid", equipmentCode))
                        .filter(QueryBuilders.prefixQuery("id", batch.getId() + "_")),
                x -> (x.equals("BatVoltage") || x.equals("BatSoh") || x.equals("BatCurrent")) ||
                        (x.equals("MaxCellVolt") || x.equals("MinCellVolt") || x.equals("MaxCellVoltNo") || x.equals("MinCellVoltNo")) ||
                        (x.equals("MaxTemp") || x.equals("MaxTempNo") || x.equals("MinTemp") || x.equals("MinTempNo")) ||
                        (x.startsWith("Cell_") && x.endsWith("_Temp")));
        if (r != null) {
            ParameterInfo t = infoMap.get("SOH");
            t.setValue(r.getDoubleValue("BatSoh"));
            t = infoMap.get("电流");
            t.setValue(r.getDoubleValue("BatCurrent"));
            t = infoMap.get("总电压");
            t.setValue(r.getDoubleValue("BatVoltage"));

            t = infoMap.get("最低单体电压");
            t.setValue(r.getDoubleValue("MinCellVolt"));
            t = infoMap.get("最低单体电压序号");
            t.setValue(r.getDoubleValue("MinCellVoltNo"));
            t = infoMap.get("最高单体电压");
            t.setValue(r.getDoubleValue("MaxCellVolt"));
            t = infoMap.get("最高单体电压序号");
            t.setValue(r.getDoubleValue("MaxCellVoltNo"));
            t = infoMap.get("电压极差");
            t.setValue(r.getDoubleValue("MaxCellVolt") - r.getDoubleValue("MinCellVolt"));

            t = infoMap.get("最高温度");
            t.setValue(r.getDoubleValue("MaxTemp"));
            t = infoMap.get("最高温度序号");
            t.setValue(r.getDoubleValue("MaxTempNo"));
            t = infoMap.get("最低温度");
            t.setValue(r.getDoubleValue("MinTemp"));
            t = infoMap.get("最低温度序号");
            t.setValue(r.getDoubleValue("MinTempNo"));
            t = infoMap.get("温度极差");
            t.setValue(r.getDoubleValue("MaxTemp") - r.getDoubleValue("MinTemp"));

            //平均温度
            t = infoMap.get("平均温度");
            double sum = 0;
            int count = 0;
            for (String key : r.keySet()) {
                if (key.startsWith("Cell_") && key.endsWith("_Temp")) {
                    sum += r.getDoubleValue(key);
                    count++;
                }
            }
            if (count == 0) {
                t.setValue(0.0);
            } else {
                t.setValue(sum / count);
            }
        }


        Tuple<Date, Date> todayRange = Utils.getTodayRange();
        Date startDate = todayRange.v1();
        Date endDate = todayRange.v2();

        // 瞬态放电电流(stfddl)  稳态放电电流(wtfddl)
        // 瞬态充电电流(stcddl) 稳态充电电流(wtcddl)
        // 最大允许充电电流(zdyxcddl) 电池包容量(dcbrl) 充电电流请求值(cddlqqz) SOC(soc) 可存储电量(kccdl)
        // 循环次数(xhcs) 电池组离散度(dczlsd)
        QDiagnoseResultInfo qDiagnoseResultInfo = QDiagnoseResultInfo.diagnoseResultInfo;
        Map<String, String> resultInfos = diagnoseResultInfoRepository.query()
                .select(qDiagnoseResultInfo.setVal, qDiagnoseResultInfo.resultVal)
                .from(qDiagnoseResultInfo)
                .where(qDiagnoseResultInfo.equipment.code.eq(equipmentCode)
                        .and(qDiagnoseResultInfo.setVal.in("stfddl", "wtfddl", "stcddl",
                                "wtcddl", "zdyxcddl", "dcbrl", "cddlqqz", "soc", "kccdl", "xhcs", "dczlsd"))
                        .and(qDiagnoseResultInfo.batch.time.between(startDate.getTime(), endDate.getTime())))
                .orderBy(qDiagnoseResultInfo.createTime.desc())
                .groupBy(qDiagnoseResultInfo.setVal)
                .limit(11)
                .transform(GroupBy.groupBy(qDiagnoseResultInfo.setVal).as(qDiagnoseResultInfo.resultVal));
        ParameterInfo diag = infoMap.get("瞬态放电电流");
        diag.setValue(NumberUtils.toDouble(resultInfos.getOrDefault("stfddl", "0"), 0));
        diag = infoMap.get("稳态放电电流");
        diag.setValue(NumberUtils.toDouble(resultInfos.getOrDefault("wtfddl", "0"), 0));
        diag = infoMap.get("瞬态充电电流");
        diag.setValue(NumberUtils.toDouble(resultInfos.getOrDefault("stcddl", "0"), 0));
        diag = infoMap.get("稳态充电电流");
        diag.setValue(NumberUtils.toDouble(resultInfos.getOrDefault("wtcddl", "0"), 0));
        diag = infoMap.get("最大允许充电电流");
        diag.setValue(NumberUtils.toDouble(resultInfos.getOrDefault("zdyxcddl", "0"), 0));
        diag = infoMap.get("电池包容量");
        diag.setValue(NumberUtils.toDouble(resultInfos.getOrDefault("dcbrl", "0"), 0));
        diag = infoMap.get("充电电流请求值");
        diag.setValue(NumberUtils.toDouble(resultInfos.getOrDefault("cddlqqz", "0"), 0));
        diag = infoMap.get("SOC");
        diag.setValue(NumberUtils.toDouble(resultInfos.getOrDefault("soc", "0"), 0));
        diag = infoMap.get("可存储电量");
        diag.setValue(NumberUtils.toDouble(resultInfos.getOrDefault("kccdl", "0"), 0));
        diag = infoMap.get("循环次数");
        diag.setValue(NumberUtils.toDouble(resultInfos.getOrDefault("xhcs", "0"), 0));
        diag = infoMap.get("电池组离散度");
        diag.setValue(NumberUtils.toDouble(resultInfos.getOrDefault("dczlsd", "0"), 0));
        return infos;
    }

    public List<JSONObject> monitorGroupList(EquipmentManage equipmentManage) {
        QEquipmentManage qEquipmentManage = QEquipmentManage.equipmentManage;
        List<EquipmentManage> groupList = equipmentManageRepository.query()
                .select(qEquipmentManage)
                .from(qEquipmentManage)
                .where(qEquipmentManage.parent.id.eq(equipmentManage.getId())
                        .and(qEquipmentManage.isValid.eq(1))
                        .and(qEquipmentManage.equipmentType.eq("3")))
                .fetch();
        Collections.sort(groupList, Comparator.comparingInt(a -> NumberUtils.toInt(a.getName().split("#")[0])));
        List<JSONObject> r = new ArrayList<>();

        JSONObject batchJson = elasticsearchService.searchNewest(QueryBuilders.boolQuery()
                .filter(QueryBuilders.termQuery("macid", equipmentManage.getCode())), name -> {
            return name.endsWith("_Temp") || name.endsWith("_Volt") || name.equals("id");
        });

        JSONObject newestData = null;
        if (batchJson != null) {
            newestData = elasticsearchService.searchByMatchFunctionNews(QueryBuilders.boolQuery()
                            .filter(QueryBuilders.termQuery("macid", equipmentManage.getCode()))
                            .filter(QueryBuilders.prefixQuery("id", batchJson.getString("id").split("_")[0] + "_")),
                    name -> {
                        return name.endsWith("_Temp") || name.endsWith("_Volt");
                    });
        }
        for (EquipmentManage em : groupList) {
            JSONObject json = new JSONObject();
            json.put("name", em.getName());
            json.put("volt", 0.0);
            json.put("temp", 0.0);

            json.put("minTemp", 0.0);
            json.put("minTempNo", "暂无数据");
            json.put("maxTemp", 0.0);
            json.put("maxTempNo", "暂无数据");

            json.put("minVolt", Integer.MAX_VALUE);
            json.put("minVoltNo", "暂无数据");

            json.put("maxVolt", 0.0);
            json.put("maxVoltNo", "暂无数据");
            if (newestData != null) {
                List<EquipmentManage> batteryList = equipmentManageRepository.findAll(
                        qEquipmentManage.parent.id.eq(em.getId())
                                .and(qEquipmentManage.isValid.eq(1))
                                .and(qEquipmentManage.equipmentType.eq("4")));

                for (EquipmentManage battery : batteryList) {
                    String voltName = battery.getCode().split("#")[1] + "Volt";
                    String tempName = battery.getCode().split("#")[1] + "Temp";
                    double voltValue = newestData.containsKey(voltName) ? Utils.convertShowVolt(newestData.getDoubleValue(voltName)) : 0;
                    double tempValue = newestData.containsKey(tempName) ? Utils.convertShowVolt(newestData.getDoubleValue(tempName)) : 0;
                    json.put("volt", json.getDoubleValue("volt") + voltValue);
                    json.put("temp", json.getDoubleValue("temp") + tempValue);
                    if (voltValue > json.getDouble("maxVolt")) {
                        json.put("maxVolt", voltValue);
                        json.put("maxVoltNo", voltName);
                    }
                    if (voltValue < json.getDouble("minVolt")) {
                        json.put("minVolt", voltValue);
                        json.put("minVoltNo", voltName);
                    }
                    if (tempValue > json.getDouble("maxTemp")) {
                        json.put("maxTemp", tempValue);
                        json.put("maxTempNo", tempName);
                    }
                    if (tempValue < json.getDouble("minTemp")) {
                        json.put("minTemp", tempValue);
                        json.put("minTempNo", tempName);
                    }
                }
            }
            r.add(json);
        }

        return r;
    }

    protected Map<JSONArray, JSONObject> constructionData(List<Double> valList) {
        if (valList.isEmpty()) {
            return new HashMap<>();
        }
        Double minVal = valList.get(0);
        Double maxVal = valList.get(valList.size() - 1);
        int numberOfX = (int) Math.ceil((maxVal - minVal) / (valList.size() + 1));
        double step = (maxVal - minVal);
        if (numberOfX > 1) {
            step = Utils.halfUp((maxVal - minVal) / (numberOfX - 1));
        }
        //生成X轴
        Double curVal = minVal;
        Map<JSONArray, JSONObject> data = new HashMap<>();
        while (curVal <= maxVal) {
            JSONObject defaultData = new JSONObject();
            Double nextVal = Utils.halfUp(curVal + step);
            defaultData.put("count", 0);
            defaultData.put("percent", 0);
            JSONArray key = new JSONArray();
            key.add(curVal);
            key.add(nextVal);
            data.put(key, defaultData);
            curVal = nextVal;
        }
        for (Double val : valList) {
            for (JSONArray key : data.keySet()) {
                if (val >= key.getDoubleValue(0) && val < key.getDoubleValue(1)) {
                    JSONObject jsonObject = data.get(key);
                    int count = jsonObject.getIntValue("count");
                    count++;
                    jsonObject.put("count", count);
                    jsonObject.put("percent", Utils.halfUp(count * 1.0 / valList.size()));
                }
            }
        }
        return data;
    }


    public JSONObject getDiagnoseFrequency(String id, String setVal, int type) {
        JSONObject r = new JSONObject();
        QEquipmentManage qEquipmentManage = QEquipmentManage.equipmentManage;
        List<String> queryIds = new ArrayList<>();
        if (type == ParameterInfo.OBJECT_NAME_BATTERY) {
            //模组
            List<String> batteryGroupIds = equipmentManageRepository
                    .query()
                    .select(qEquipmentManage.id)
                    .from(qEquipmentManage)
                    .where(qEquipmentManage.parent.id.eq(id)
                            .and(qEquipmentManage.equipmentType.eq("3"))
                            .and(qEquipmentManage.isValid.eq(1)))
                    .fetch();
            //单体
            queryIds = equipmentManageRepository.query()
                    .select(qEquipmentManage.id)
                    .from(qEquipmentManage)
                    .where(qEquipmentManage.parent.id.in(batteryGroupIds)
                            .and(qEquipmentManage.equipmentType.eq("4")
                                    .and(qEquipmentManage.isValid.eq(1))))
                    .fetch();
        } else {
            //电池包
            queryIds.add(id);
        }
        String sql = "SELECT * FROM esms_diagnose_result_info WHERE id " +
                " IN(SELECT MAX(id) FROM esms_diagnose_result_info WHERE set_val LIKE ? " +
                " AND equipment_id IN ('" + StringUtils.join(queryIds, "','") + "') GROUP BY set_val)";
        Query qry = entityManager.createNativeQuery(sql);
        qry.setParameter(1, setVal);
        //as map
        qry.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        List<Map> list = qry.getResultList();
        List<Double> valList = new ArrayList<>();
        for (Map map : list) {
            Double val = Double.parseDouble((String) map.get("result_val"));
            valList.add(val);
        }
        Collections.sort(valList);
        r.put("data", this.constructionData(valList));
        return r;
    }

    public JSONObject getSocFrequency(String id) {
        return this.getDiagnoseFrequency(id, "dtsoc\\_%", ParameterInfo.OBJECT_NAME_BATTERY);
    }

    public JSONObject getCorrectingFrequency(String id) {
        return this.getDiagnoseFrequency(id, "dtzz\\_%", ParameterInfo.OBJECT_NAME_BATTERY);
    }
}
